module(..., package.seeall) -- 使得文件中的函数在何处都可调用
require "pins" -- 用到了pin库，该库为luatask专用库，需要进行引用
require "sys"
require "getLbsLoc"
require "rtos"
require "utils"
require "mqtt"
require "misc"
require "audio"
require "math"
require "nvm"
require "modbusrtu"
-- 初始化设备参数文件
nvm.init("config.lua", true)
-- 参数初始化 magnitude:震级
local sysTime, eqlog, errlog, eventID, focal_longitude, focal_latitude,
      quake_intensity, quakeTimeStr, magnitude, msgCode, magnitudeDevice,
      doTask, taskBreak = "", "", "", "", 0.0, 0.0, 0.0, "", 0.0, "", 0.0,
                          false, false
TTS = 1
local setGpio16Fnc = pins.setup(pio.P0_16, 1)     -- 电磁阀吸合 0-正常 1-吸合
local setGpio17Fnc = pins.setup(pio.P0_17, 1)     -- 报警灯 0-灯灭 1-灯亮
-- 获取配置参数
local longitude, latitude, deviceMagnitude, configIntensity,
      earthquakeOpenOrnot, volumeLevel = nvm.get("longitude"),
                                         nvm.get("loatitude"),
                                         tonumber(nvm.get("deviceMagnitude")),
                                         tonumber(nvm.get("configIntensity")),
                                         nvm.get("earthquakeOpenOrnot"),
                                         nvm.get("volumeLevel")

-- 预警事件ID组装
local function warningEventIdAssemble(quakeTimeStr)
    local datePre, timeSur, y, m, d, h, M, s = "", "", "", "", "", "", "", ""
    datePre = string.split(quakeTimeStr, " ")[1]
    timeSur = string.split(quakeTimeStr, " ")[2]
    y = string.split(datePre, "-")[1]
    m = string.split(datePre, "-")[2]
    d = string.split(datePre, "-")[3]
    h = string.split(timeSur, ":")[1]
    M = string.split(timeSur, ":")[2]
    s = string.split(timeSur, ":")[3]
    eventID = y .. m .. d .. h .. M .. s .. ".0001_1"
    return eventID
end
-- 获取系统时间
local function getSystemTime()
    local tClock, nowDate = os.date("*t"), ""
    nowDate = string.format("%04d-%02d-%02d %02d:%02d:%02d", tClock.year,
                            tClock.month, tClock.day, tClock.hour, tClock.min,
                            tClock.sec)
    return nowDate
end

-- string转time
local function string2time(str)
    log.info("str", str)
    local dateStr, timeStr = "", ""
    dateStr = string.split(str, " ")[1]
    timeStr = string.split(str, " ")[2]
    local Y = string.split(dateStr, "-")[1]
    local m = string.split(dateStr, "-")[2]
    local d = string.split(dateStr, "-")[3]
    local H = string.split(timeStr, ":")[1]
    local M = string.split(timeStr, ":")[2]
    local S = string.split(timeStr, ":")[3]
    log.info("Y:", Y, "m:", m, "d:", d, "H:", H, "M:", M, "S:", S)
    return os.time({day = d, month = m, year = Y, hour = H, min = M, sec = S})
end

local function round(x)
    return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)
end

local function Algorithm()
    local Lat1 = math.rad(latitude)
    log.info("focal_latitude:", focal_latitude)
    local Lat2 = math.rad(focal_latitude)
    local a = Lat1 - Lat2
    local b = math.rad(longitude) - math.rad(focal_longitude)
    local s = 2 *
                  math.asin(
                      math.sqrt(math.pow(math.sin(a / 2), 2) + math.cos(Lat1) *
                                    math.cos(Lat2) *
                                    math.pow(math.sin(b / 2), 2)))
    s = s * 6378137.0
    s = round(s * 10000) / 10000; -- 精确距离的数值
    return s
end
-- 烈度转换为地震等级
local function intensity2SeismicCategory(intensity2Th)
    -- 利用公式求出设备所在地的地震等级(华北地区，b半径)
    magnitudeDevice = (intensity2Th - 2.008 + 2.958 * math.log10(7)) / 1.434
    return magnitudeDevice
end

-- 预警参数组装
local function alert_parameter_assembly(tjsondata)
    -- keson地震预警监控平台
    if tjsondata["7"] then focal_longitude = tonumber(tjsondata["7"]) end
    if tjsondata["8"] then focal_latitude = tonumber(tjsondata["8"]) end
    if tjsondata["11"] then quake_intensity = tonumber(tjsondata["11"]) end
    if tjsondata["5"] then
        quakeTimeStr = tjsondata["5"]
        eventID = warningEventIdAssemble(quakeTimeStr)
    end
    if tjsondata["10"] then magnitude = tonumber(tjsondata["10"]) end
    if tjsondata["16"] then msgCode = tjsondata["16"] end
end
-- 电磁阀动作
local function electromagneticValveOperation(countDownsEarthquake)
    doTask = true
    setGpio16Fnc(0)
    setGpio17Fnc(0)
    modbusrtu.Modbus_send("0x01", "0x06", "0x0013", "136")
    while countDownsEarthquake >= 0 do
        if taskBreak then
            taskBreak = false
            break
        end
        log.info("countDownsEarthquake:", countDownsEarthquake)
        sys.wait(1000)
        countDownsEarthquake = countDownsEarthquake - 1
        if drillBreak then
            taskBreak = false
            break
        end
    end
    setGpio16Fnc(1)
    setGpio17Fnc(1)
    modbusrtu.Modbus_send("0x01", "0x06", "0x0013", "128")
    doTask = false
end
-- 地震预警动作执行
local function earthquake_alert_action(aesDecryptMsg)
    eqlog = eqlog .. "[" .. sysTime .. "]" ..
                "接收到的地震预警消息内容:" .. "\n" .. aesDecryptMsg
    local sysTimeOs = os.time()
    log.info("sysTime", sysTimeOs, "quakeTimeStr", quakeTimeStr)
    local quakeTime = string2time(quakeTimeStr)
    log.info("quakeTime", quakeTime)
    -- 计算距离
    local distance = Algorithm() / 1000
    log.info("distance", distance)
    -- 计算S波到达时间 减1秒网络延时
    local countDownS = math.floor((distance / 3.5) - (sysTimeOs - quakeTime))
    countDownS = countDownS < 0 and 0 or countDownS
    log.info("countDownS", countDownS)
    -- 烈度计算
    local intensityLocal = 0.0
    intensityLocal = math.floor(round(quake_intensity - 4 *
                                          math.log10(distance / 10 + 1.0)))
    if intensityLocal <= 0 then intensityLocal = 1 end
    log.info("intensityLocal", intensityLocal, "configIntensity:",
             configIntensity, "deviceMagnitude", deviceMagnitude,
             "intensity2SeismicCategory(intensityLocal)",
             intensity2SeismicCategory(intensityLocal))
    eqlog = eqlog .. "[" .. sysTime .. "]" .. "[地震预警信息:]" ..
                "震源距离:" .. distance .. "KM,s波到达时间:" ..
                countDownS .. "s,烈度:" .. intensityLocal .. "\n"
    -- 地震烈度大于等于设定预警临界值则执行报警
    if intensityLocal >= configIntensity or
        intensity2SeismicCategory(intensityLocal) >= deviceMagnitude then
        -- sys.electromagneticValveAction()   --电磁阀动作
        eqlog = eqlog .. "[" .. sysTime .. "]" ..
                    "本地计算-达到本地预警阀值！\n"
        if countDownS >= 0 then
            if "0101" == msgCode or "0102" == msgCode then -- 普通预警，专业预警,keson转发预警
                eqlog = eqlog .. "[" .. sysTime .. "]" ..
                            "本地计算-达到本地预警音阀值！\n"
                sys.wait(300)
                if earthquakeOpenOrnot then -- 地震预警打开
                    sys.taskInit(electromagneticValveOperation,countDownS)
                end
            end
        end
    else
        eqlog = eqlog .. "[" .. sysTime .. "]" ..
                    "[不满足预警临界值：]" .. "烈度：" ..
                    intensityLocal .. ",烈度预设值：" .. configIntensity ..
                    ",震级:" .. magnitude .. ",震中烈度:" ..
                    quake_intensity .. "\n"
    end
end

-- 地震预警主逻辑
function EarthquakeWarning(aesDecryptMsg, tjsondata)
    if doTask then taskBreak = true end
    while doTask do sys.wait(100) end
    longitude, latitude, deviceMagnitude, configIntensity, earthquakeOpenOrnot =
        nvm.get("Longitude"), nvm.get("Latitude"), nvm.get("DeviceMagnitude"),
        nvm.get("ConfigIntensity"), nvm.get("EarthquakeOpenOrnot")
    errlog, eqlog = "", ""
    sysTime = getSystemTime()
    alert_parameter_assembly(tjsondata)
    log.info("Equipment_latitude", getLbsLoc.Lat, "Equipment_longitude",
             getLbsLoc.Lng)
    if longitude == "" and latitude == "" then
        longitude = getLbsLoc.Lng
        latitude = getLbsLoc.Lat
    end
    eqlog =
        eqlog .. "[" .. sysTime .. "]" .. "[设备位置信息:]" .. "Lng:" ..
            longitude .. ",Lat:" .. latitude .. "\n"
    -- keson转发地震预警信息
    if "0199" == msgCode or quake_intensity == 0 then
        if doTask then taskBreak = true end
    else
        sys.taskInit(earthquake_alert_action, aesDecryptMsg)
    end
    if eqlog ~= nil and eqlog ~= false then
        -- .generateLogTask(eqlog, "log")
    end
    if errlog ~= "" and errlog ~= false then
        -- logGenerator.generateLogTask(errlog, "log")
    end
end
